package com.uh.rds.testing.validator;

import com.uh.rds.testing.base.DataValidator;
import com.uh.rds.testing.logger.TestLoggerFactory;
import org.slf4j.Logger;
import redis.clients.jedis.Jedis;

import java.util.*;

public abstract class DataValidatorBase implements DataValidator {
    protected final Logger logger = TestLoggerFactory.getLogger(this.getClass());
    protected final Logger clearLogger = TestLoggerFactory.getLogger(this.getClass()); // 删除数据的日志(每个logger都有输出error的限制）

    protected Integer index;
    protected int shardIndex;
    protected String name;
    protected Jedis processJedis; // 处理数据的连接
    protected Jedis validateJedis; // 验证数据的连接
    protected volatile int size; // 整体数据量
    protected volatile int procSize; // 处理过的数据量(一般是插入的数据量)
    protected volatile int validSize; // 验证通过的数据量

    protected volatile int clearSize; // 完成清理的数据量
    protected volatile long processTime = 0; // 处理数据的时间
    protected volatile long validateTime = 0; // 设置过期的时间


    protected DataValidatorArguments arguments; // 配置信息

    protected Map<String, List<String[]>> data;

    protected List<String> reversedKeys; // 反向验证时使用

    /*
     * 下面是运行时变量
     */
    protected volatile long progressBeginTime; // 处理开始的时间，用于记录并显示处理、验证的进度。
    protected volatile int progressSize; //已经处理过的数据量，用于记录并显示处理、验证的进度。
    protected int progressStageSize = -1; // 每次记录阶段的数据量


    @Override
    public int getSize() {
        return size;
    }

    @Override
    public int getProcSize() {
        return procSize;
    }

    @Override
    public int getValidSize() {
        return validSize;
    }

    @Override
    public int getClearSize() {
        return clearSize;
    }

    @Override
    public long getValidateTime() {
        return validateTime;
    }

    @Override
    public long getProcessTime() {
        return processTime;
    }

    @Override
    public void init(Integer index, int shardIndex, DataValidatorArguments arguments, Map<String, List<String[]>> data) {
        this.index = index;
        this.shardIndex = shardIndex;
        this.name = null;
        this.clearSize = 0;
        this.procSize = 0;
        this.validSize = 0;
        this.arguments = arguments;
        this.data = data;
        this.size = data.size();
        this.procSize = this.size;
        reverseKeys(data, arguments);
        //初始化 stepSize
        initStepSize();

        logger.info("{} Validator was created! Data size:{}", getName(), data.size());
    }

    @Override
    public String getName() {
        if(this.name == null) {
            if(shardIndex >= 0) {
                this.name = "[Thread" + index + "_Shard" + shardIndex + "_" + getType() + "]";
            }
            else {
                this.name = "[Thread" + index + "_" + getType() + "]";
            }
        }
        return name;
    }

    public void setArguments(DataValidatorArguments arguments) {
        this.arguments = arguments;
    }

    @Override
    public void setRuntime(Jedis processJedis, Jedis validateJedis) {
        this.processJedis = processJedis;
        this.validateJedis = validateJedis;
    }

    /**
     * 对多个key设置过期时间。
     * @param jedis jedis 对象。
     * @param keys 要设置过期的key集合
     * @param expireSeconds 过期的时间（秒）
     * @return
     */
    protected long expire(Jedis jedis, Set<String> keys, long expireSeconds) {
        long effectedKeys = 0; //生效的数量
        for(String key : keys) {

            long timeout = jedis.expire(key, expireSeconds);
            if(timeout > 0) {
                effectedKeys ++;
            }
        }
        return effectedKeys;
    }

    public boolean validateExpired(Jedis jedis, Set<String> keys) {
        validSize = keys.size();
        for(String key : keys) {
            if(jedis.exists(key)) {
                validSize --; //存在的key不算
                logger.error("{} key[{}] validate failed! It expected to be expired, but exists!", getName(), key);
            }
        }

        boolean passed = (procSize == validSize);
        if(passed) {
            logger.info(getName() + " validateExpired passed! validate size:" + validSize);
        } else {
            logger.error(getName() + " validateExpired failed! Expected size:" + procSize + " actual size:" + validSize);
        }

        return passed;
    }


    public long clear(Jedis jedis, Jedis validateJedis, Set<String> keys) {
        clearLogger.info("{} begin clear data, keys count:{}", getName(), keys.size());

        for(String key : keys) {
            long r = jedis.del(key);
            if(r == 1) {
                clearSize ++;
            }
            else {
                clearLogger.error("{} clear data exception key:{} return:{}", getName(), key, r);
            }
        }

        clearLogger.info("{} clear data finished, clearSize:{}. Clear same data again to validate.", getName(), clearSize);

        try{
            Thread.sleep(arguments.getValidateInterval() * 2); // 等待一段时间，确保数据被清理
        } catch (InterruptedException e) {
            logger.error("Thread sleep error", e);
        }

        //校验过程是否翻转keys的顺序
        Collection<String> validateKeys = arguments.isValidateReversed() ? reversedKeys : keys;

        // 测试，清理数据后再次清理, 验证上次删是否有效
        for(String key : validateKeys) {
            long del = validateJedis.del(key);
            if(del > 0) {
                if(clearSize > 0) clearSize --;
                clearLogger.error("{} clear data validate exception key:{} return:{}", getName(), key, del);
            }
        }

        clearLogger.info("{} clear keys count={}", getName(), clearSize);

        return clearSize;
    }


    protected void processBeginLog() {
        this.procSize = 0;
        this.progressSize = 0;
        this.progressBeginTime = System.currentTimeMillis();
        logger.info("{} process begin dataSize={}", getName(), size);
    }


    /**
     * 处理数据进度记录日志
     */
    protected void processingLog(boolean succeed) {
        long keyInterval = arguments.getEachKeyInterval();
        if(keyInterval > 0) {
            try { Thread.sleep(keyInterval); }
            catch (InterruptedException e) {  logger.error("Thread sleep error", e); }
        }

        if(succeed) {
            this.procSize++;
        }

        this.progressSize ++;

        if(progressStageSize != 0 && progressSize % progressStageSize == 0) {
            logger.info("{} processing, dataSize={}, proceedData={}, succeedData={}", getName(), size, progressSize, procSize);
        }
    }


    protected void processEndLog() {
        processTime = System.currentTimeMillis() - progressBeginTime;
        logger.info("{} process end, dataSize={}, proceedData:{} takeUpTime:{}ms", getName(), size, procSize, processTime);
    }


    protected void validateBeginLog() {
        this.validSize = 0;
        this.progressSize = 0;
        this.progressBeginTime = System.currentTimeMillis();
        logger.info("{} validate begin dataSize={}", getName(), size);
    }


    /**
     * 处理数据进度记录日志
     */
    protected void validatingLog(boolean succeed) {
        long keyInterval = arguments.getEachKeyInterval();
        if(keyInterval > 0) {
            try { Thread.sleep(keyInterval); }
            catch (InterruptedException e) {  logger.error("Thread sleep error", e); }
        }

        if(succeed) {
            this.validSize++;
        }

        this.progressSize ++;

        if(progressStageSize != 0 && progressSize % progressStageSize == 0) {
            logger.info("{} validating, dataSize={}, validatedData={}, passedData={}", getName(), size, progressSize, validSize);
        }
    }

    /**
     * 在validate执行完成后，进行运行时长记录和输出结果日志
     * @return
     */
    protected boolean validateEndLog() {
        validateTime = System.currentTimeMillis() - progressBeginTime;
        boolean passed = (procSize == validSize);

        if(passed) {
            logger.info("{} validate passed! dataSize={}, validatedData:{} takeUpTime:{}ms", getName(), size, validSize, validateTime);
        } else {
            logger.error("{} validate failed! dataSize={}, validatedData:{} takeUpTime:{}ms", getName(), size, validSize, validateTime);
        }

        return passed;
    }


    /**
     * 反向验证时，反转key的顺序
     * config.isValidateReversed() 为true时，才会反转key的顺序
     * @param data
     * @param arguments
     */
    protected void reverseKeys(Map<String, ?> data, DataValidatorArguments arguments) {
        if(arguments.isValidateReversed()) {
            List<String> keys = new ArrayList<>(data.keySet());
            Collections.reverse(keys);
            this.reversedKeys = keys;
        }
        else {
            this.reversedKeys = null;
        }
    }



    private int initStepSize() {
        if(size >= 50000) {
            progressStageSize = 10000;
        }
        else if(size >= 40000) {
            progressStageSize = 8000;
        }
        else if(size >= 20000) {
            progressStageSize = 4000;
        }
        else if(size >= 10000) {
            progressStageSize = 2000;
        }
        else if(size >= 5000) {
            progressStageSize = 1000;
        }
        else if(size >= 4000) {
            progressStageSize = 800;
        }
        else if(size >= 2000) {
            progressStageSize = 400;
        }
        else if(size >= 1000) {
            progressStageSize = 200;
        }
        else {
            progressStageSize = 0;
        }

        return progressStageSize;
    }



    public String toString() {
        return getName() + "{ size:" + size + ", procSize:" + procSize + ", validSize:" + validSize + ", clearSize:" + clearSize + "}";
    }

}
